(function (angular) {
    'use strict';
    var formatModule = angular.module('fs.format');
    formatModule.directive('fsNumFormat', [
        '$parse',
        'formatService',
        'storageService',
        'keydownService',
        'FormatterConstants',
        function ($parse, formatService, storageService, keydownService, FormatterConstants) {
            return {
                restrict: 'A',
                require: ['ngModel', '^^?fsGrid'],
                link: function ($scope, $element, $attr, ctrls) {
                    var ngModelCtrl = ctrls[0],
                        fsGridCtrl = ctrls[1];
                    linkFunction($scope, $element, $attr, ngModelCtrl, fsGridCtrl, 'number', $parse, formatService, storageService, keydownService, FormatterConstants);
                }
            };
        }
    ]);
    formatModule.directive('fsMoneyFormat', [
        '$parse',
        'formatService',
        'storageService',
        'keydownService',
        'FormatterConstants',
        function ($parse, formatService, storageService, keydownService, FormatterConstants) {
            return {
                restrict: 'A',
                require: ['ngModel', '^^?fsGrid'],
                link: function ($scope, $element, $attr, ctrls) {
                    var ngModelCtrl = ctrls[0],
                        fsGridCtrl = ctrls[1];
                    linkFunction($scope, $element, $attr, ngModelCtrl, fsGridCtrl, 'money', $parse, formatService, storageService, keydownService, FormatterConstants);
                }
            };
        }
    ]);
    formatModule.directive('fsPctFormat', [
        '$parse',
        'formatService',
        'storageService',
        'keydownService',
        'FormatterConstants',
        function ($parse, formatService, storageService, keydownService, FormatterConstants) {
            return {
                restrict: 'A',
                require: ['ngModel', '^^?fsGrid'],
                link: function ($scope, $element, $attr, ctrls) {
                    var ngModelCtrl = ctrls[0],
                        fsGridCtrl = ctrls[1];
                    linkFunction($scope, $element, $attr, ngModelCtrl, fsGridCtrl, 'percentage', $parse, formatService, storageService, keydownService, FormatterConstants);
                }
            };
        }
    ]);
    formatModule.directive('fsSstFormat', [
        '$filter',
        '$parse',
        'formatService',
        'storageService',
        'keydownService',
        'FormatterConstants',
        function ($filter, $parse, formatService, storageService, keydownService, FormatterConstants) {
            return {
                restrict: 'A',
                require: ['ngModel', '^^?fsGrid'],
                link: function ($scope, $element, $attr, ctrls) {
                    var ngModelCtrl = ctrls[0],
                        fsGridCtrl = ctrls[1];
                    linkFunction($scope, $element, $attr, ngModelCtrl, fsGridCtrl, 'sensitive', $parse, formatService, storageService, keydownService, FormatterConstants);
                }
            };
        }
    ]);
    formatModule.directive('fsDateFormat', [
        '$parse',
        'formatService',
        'storageService',
        'keydownService',
        'FormatterConstants',
        function ($parse, formatService, storageService, keydownService, FormatterConstants) {
            return {
                restrict: 'A',
                require: ['ngModel', '^^?fsGrid'],
                link: function ($scope, $element, $attr, ctrls) {
                    var ngModelCtrl = ctrls[0],
                        fsGridCtrl = ctrls[1];
                    linkFunction($scope, $element, $attr, ngModelCtrl, fsGridCtrl, 'date', $parse, formatService, storageService, keydownService, FormatterConstants);
                }
            };
        }
    ]);
    formatModule.directive('fsUppercase', [
        '$filter',
        function ($filter) {
            return {
                restrict: 'A',
                require: 'ngModel',
                link: function ($scope, $element, $attr, ngModelCtrl) {
                    if ($scope.$eval($attr.fsUppercase) === true || $filter('uppercase')($attr.fsUppercase) == 'TRUE') {
                        ngModelCtrl.$parsers.push(function (input) {
                            var capitalized = angular.isString(input) ? $filter('uppercase')(input) : input;
                            if (capitalized !== input) {
                                // see where the cursor is before the update so that we can set it back
                                var selection = getCursortPosition($element[0]);
                                ngModelCtrl.$setViewValue(capitalized);
                                ngModelCtrl.$render();
                                // set back the cursor after rendering
                                $element[0].selectionStart = selection;
                                $element[0].selectionEnd = selection;
                            }
                            return capitalized;
                        });
                    }
                }
            };
        }
    ]);

    function linkFunction($scope, $element, $attr, ngModelCtrl, fsGridCtrl, formatType, $parse, formatService, storageService, keydownService, FormatterConstants) {
        var pattern, getter, setter, value, watcher, watchers = [], needFormat = true, defaultFormat = FormatterConstants.DEFAULT_FORMATTER;
        var isEmpty = function (value) {
            return value === null || angular.isUndefined(value) || (angular.isString(value) ? value.trim() == '' : false);
        };
        var getPattern = function (formatType) {
            var _pattern, fsFormat;
            if (formatType == 'number') {
                fsFormat = $attr.fsNumFormat;
            } else if (formatType == 'money') {
                fsFormat = $attr.fsMoneyFormat;
            } else if (formatType == 'percentage') {
                fsFormat = $attr.fsPctFormat;
            } else if (formatType == 'date') {
                fsFormat = $attr.fsDateFormat;
            } else if (formatType == 'sensitive') {
                fsFormat = $attr.fsSstFormat;
            }
            if (fsFormat && (formatType == 'number' || formatType == 'money' || formatType == 'percentage') && fsFormat.match('[#0]')) {
                _pattern = fsFormat;
            } else if (formatType == 'date' && fsFormat.indexOf('yyyy') != -1) {
                _pattern = fsFormat;
            } else {
                // for numberFormat the _pattern may be 'vm.ratingType.scoreDecimal' or '#0.0' or 'defaultInteger' etc.
                // for moneyFormat the pattern may be 'USD' or 'vm.product.loanAmtRangeCcy' or 'loanAmtRangeCcy' or 'moeny0' etc
                if (!fsFormat) {
                    return null;
                }
                _pattern = $parse(fsFormat)(fsGridCtrl.fsGridConfig ? fsGridCtrl.fsGridConfig.appScope : fsGridCtrl);
                if (isEmpty(_pattern)) _pattern = $parse(fsFormat)(fsGridCtrl.ngModel);
                //for sensitive
                if (isEmpty(_pattern)) _pattern = $parse(fsFormat)(fsGridCtrl);
            }
            return _pattern || fsFormat;
        };
        $scope.$on('$destroy', function () {
            while (watchers.length) {
                watchers.shift()();
            }
        });

        getter = $parse($attr.ngModel.match(/ngModel.(\S*)/)[1]);
        setter = getter.assign;
        if (formatType == 'number' || formatType == 'money' || formatType == 'percentage') {
            ngModelCtrl.$parsers.push(function (value) {
                var formatInfo = storageService.getFormatInfo() || {},
                    groupSeparator = formatInfo.groupSeparator || ',',
                    decimalSeparator = formatInfo.decimalSeparator || '.',
                    groupReg = new RegExp('\\' + groupSeparator, 'g'),
                    decimalReg = new RegExp('\\' + decimalSeparator);
                if (value.indexOf(groupSeparator) > -1) {
                    value = value.replace(groupReg, '');
                }
                if (value.match(decimalSeparator) && value.match(decimalSeparator).length == 1) {
                    value = value.replace(decimalReg, '.');
                }
                return value;
            });
        }

        watcher = $scope.$watch(
            // TODO : wacth $attr.ngModel ??
            // 'formComponentCtrl.ngModel.' + fsGridCtrl.fsGridConfig.model,
            $attr.ngModel,
            function (value, oldValue) {
                if (needFormat && !isEmpty(value)) {
                    if (angular.isNumber(value) && value == 0) {
                        if (fsGridCtrl.fsGridConfig.filter && fsGridCtrl.fsGridConfig.filter == 'zeroEmpty') {
                            ngModelCtrl.$viewValue = '';
                            $element.val('');
                            return;
                        }
                    }
                    if (formatType == 'number') {
                        pattern = getPattern(formatType);
                        value = formatService.formatNumber(value, pattern);
                        setter(fsGridCtrl.ngModel, formatService.parseNumber(value));
                        $element.val(value);
                    } else if (formatType == 'money') {
                        pattern = getPattern(formatType);
                        value = formatService.formatMoney(value, pattern);
                        setter(fsGridCtrl.ngModel, formatService.parseMoney(value));
                        $element.val(value);
                    } else if (formatType == 'percentage') {
                        pattern = getPattern(formatType);
                        value = formatService.formatPercentage(value, pattern);
                        setter(fsGridCtrl.ngModel, formatService.parsePercentage(value));
                        $element.val(value);
                    } else if (formatType == 'date') {
                        pattern = getPattern(formatType);
                        value = formatService.formatDate(value, pattern);
                        $element.val(value);
                    } else if (formatType == 'sensitive') {
                        pattern = getPattern(formatType);
                        value = formatService.formatSst(value, pattern);
                        $element.val(value);
                    }
                }
            },
            true
        );
        watchers.push(watcher);
        try {
            if (formatType == 'number') {
                //--for format like 'vm.feeCode.feeCcy'
                watcher = fsGridCtrl.fsGridConfig.appScope.$watch($attr.fsNumFormat, function (pattern) {
                    if (needFormat && !isEmpty(pattern)) {
                        value = getter(fsGridCtrl.ngModel);
                        if (angular.isNumber(value) && value == 0 && ngModelCtrl.$viewValue == '') {
                            if (fsGridCtrl.fsGridConfig.filter && fsGridCtrl.fsGridConfig.filter == 'zeroEmpty') {
                                $element.val('');
                                return;
                            }
                        }
                        if (!isEmpty(value)) {
                            value = formatService.formatNumber(value, pattern);
                            setter(fsGridCtrl.ngModel, formatService.parseNumber(value));
                            $element.val(value);
                        }
                    }
                });
                watchers.push(watcher);
                // TODO:watch formComponentCtrl.ngModel ??
                watcher = $scope.$watch('formComponentCtrl.ngModel.' + $attr.fsNumFormat, function (pattern) {
                    if (needFormat && !isEmpty(pattern)) {
                        value = getter(fsGridCtrl.ngModel);
                        if (angular.isNumber(value) && value == 0 && ngModelCtrl.$viewValue == '') {
                            if (fsGridCtrl.fsGridConfig.filter && fsGridCtrl.fsGridConfig.filter == 'zeroEmpty') {
                                $element.val('');
                                return;
                            }
                        }
                        if (!isEmpty(value)) {
                            value = formatService.formatNumber(value, pattern);
                            setter(fsGridCtrl.ngModel, formatService.parseNumber(value));
                            $element.val(value);
                        }
                    }
                });
                watchers.push(watcher);
            } else if (formatType == 'money') {
                watcher = fsGridCtrl.fsGridConfig.appScope.$watch($attr.fsMoneyFormat, function (pattern) {
                    if (needFormat && !isEmpty(pattern)) {
                        value = getter(fsGridCtrl.ngModel);
                        if (angular.isNumber(value) && value == 0 && ngModelCtrl.$viewValue == '') {
                            if (fsGridCtrl.fsGridConfig.filter && fsGridCtrl.fsGridConfig.filter == 'zeroEmpty') {
                                $element.val('');
                                return;
                            }
                        }
                        if (!isEmpty(value)) {
                            value = formatService.formatMoney(value, pattern);
                            setter(fsGridCtrl.ngModel, formatService.parseMoney(value));
                            $element.val(value);
                        }
                    }
                });
                watchers.push(watcher);
                // TODO:watch formComponentCtrl.ngModel ??
                watcher = $scope.$watch('formComponentCtrl.ngModel.' + $attr.fsMoneyFormat, function (pattern) {
                    if (needFormat && !isEmpty(pattern)) {
                        value = getter(fsGridCtrl.ngModel);
                        if (angular.isNumber(value) && value == 0 && ngModelCtrl.$viewValue == '') {
                            if (fsGridCtrl.fsGridConfig.filter && fsGridCtrl.fsGridConfig.filter == 'zeroEmpty') {
                                $element.val('');
                                return;
                            }
                        }
                        if (!isEmpty(value)) {
                            value = formatService.formatMoney(value, pattern);
                            setter(fsGridCtrl.ngModel, formatService.parseMoney(value));
                            $element.val(value);
                        }
                    }
                });
                watchers.push(watcher);
            } else if (formatType == 'sensitive') {
                // TODO:watch formComponentCtrl.ngModel ??
                watcher = $scope.$watch('formComponentCtrl.ngModel.' + $attr.fsSstFormat, function (pattern) {
                    if (needFormat && !isEmpty(pattern)) {
                        value = getter(fsGridCtrl.ngModel);
                        if (!isEmpty(value)) {
                            value = formatService.formatSst(value, pattern);
                            $element.val(value);
                        }
                    }
                });
                watchers.push(watcher);
            } else if (formatType == 'percentage') {
                watcher = fsGridCtrl.fsGridConfig.appScope.$watch($attr.fsPctFormat, function (pattern) {
                    if (needFormat && !isEmpty(pattern)) {
                        value = getter(fsGridCtrl.ngModel);
                        if (angular.isNumber(value) && value == 0 && ngModelCtrl.$viewValue == '') {
                            if (fsGridCtrl.fsGridConfig.filter && fsGridCtrl.fsGridConfig.filter == 'zeroEmpty') {
                                $element.val('');
                                return;
                            }
                        }
                        if (!isEmpty(value)) {
                            value = formatService.formatPercentage(value, pattern);
                            setter(fsGridCtrl.ngModel, formatService.parsePercentage(value));
                            $element.val(value);
                        }
                    }
                });
                watchers.push(watcher);
                // TODO:watch formComponentCtrl.ngModel ??
                watcher = $scope.$watch('formComponentCtrl.ngModel.' + $attr.fsPctFormat, function (pattern) {
                    if (needFormat && !isEmpty(pattern)) {
                        value = getter(fsGridCtrl.ngModel);
                        if (angular.isNumber(value) && value == 0 && ngModelCtrl.$viewValue == '') {
                            if (fsGridCtrl.fsGridConfig.filter && fsGridCtrl.fsGridConfig.filter == 'zeroEmpty') {
                                $element.val('');
                                return;
                            }
                        }
                        if (!isEmpty(value)) {
                            value = formatService.formatPercentage(value, pattern);
                            setter(fsGridCtrl.ngModel, formatService.parsePercentage(value));
                            $element.val(value);
                        }
                    }
                });
                watchers.push(watcher);
            } else if (formatType == 'date') {
                watcher = $scope.fsGridConfig.appScope.$watch($attr.fsDateFormat, function (pattern) {
                    if (needFormat && !isEmpty(pattern)) {
                        value = getter(fsGridCtrl.ngModel);
                        if (!isEmpty(value)) {
                            value = formatService.formatDate(value, pattern);
                            setter(fsGridCtrl.ngModel, formatService.parseDate(value, pattern));
                            $element.val(value);
                        }
                    }
                });
                watchers.push(watcher);
                // TODO:watch formComponentCtrl.ngModel ??
                watcher = $scope.$watch('formComponentCtrl.ngModel.' + $attr.fsDateFormat, function (pattern) {
                    if (needFormat && !isEmpty(pattern)) {
                        value = getter(fsGridCtrl.ngModel);
                        if (!isEmpty(value)) {
                            value = formatService.formatDate(value, pattern);
                            setter(fsGridCtrl.ngModel, formatService.parseDate(value, pattern));
                            $element.val(value);
                        }
                    }
                });
                watchers.push(watcher);
            }
        } catch (e) { }

        $element.bind('focus', function (e) {
            needFormat = false;
            if (formatType != 'date') {
                if (angular.isNumber(getter(fsGridCtrl.ngModel)) && getter(fsGridCtrl.ngModel) == 0 && ngModelCtrl.$viewValue == '') {
                    if (fsGridCtrl.fsGridConfig.filter && fsGridCtrl.fsGridConfig.filter == 'zeroEmpty') {
                        return;
                    }
                }
                // see where the cursor is before the update so that we can set it back
                var selection = getCursortPosition($element[0]);
                $element.val(getter(fsGridCtrl.ngModel));
                // set back the cursor after rendering
                $element[0].selectionStart = selection;
                $element[0].selectionEnd = selection;
            }
        });

        $element.bind('blur', function (e) {
            needFormat = true;
            value = getter(fsGridCtrl.ngModel);
            if (formatType == 'number' && !isEmpty(value)) {
                if (angular.isNumber(value) && value == 0 && ngModelCtrl.$viewValue == '') {
                    if (fsGridCtrl.fsGridConfig.filter && fsGridCtrl.fsGridConfig.filter == 'zeroEmpty') {
                        return;
                    }
                }
                pattern = getPattern(formatType);
                value = formatService.formatNumber(value, pattern);
                if (!isEmpty(value)) {
                    setter(fsGridCtrl.ngModel, formatService.parseNumber(value));
                    $element.val(value);
                } else {
                    setter(fsGridCtrl.ngModel, '');
                    $element.val('');
                }
            } else if (formatType == 'money' && !isEmpty(value)) {
                if (angular.isNumber(value) && value == 0 && ngModelCtrl.$viewValue == '') {
                    if (fsGridCtrl.fsGridConfig.filter && fsGridCtrl.fsGridConfig.filter == 'zeroEmpty') {
                        return;
                    }
                }
                pattern = getPattern(formatType);
                value = formatService.formatMoney(value, pattern);
                if (!isEmpty(value)) {
                    setter(fsGridCtrl.ngModel, formatService.parseMoney(value));
                    $element.val(value);
                } else {
                    setter(fsGridCtrl.ngModel, '');
                    $element.val('');
                }
            } else if (formatType == 'sensitive' && !isEmpty(value)) {
                pattern = getPattern(formatType);
                value = formatService.formatSst(value, pattern);
                if (!isEmpty(value)) {
                    $element.val(value);
                } else {
                    $element.val('');
                }
            } else if (formatType == 'percentage' && !isEmpty(value)) {
                if (angular.isNumber(value) && value == 0 && ngModelCtrl.$viewValue == '') {
                    if (fsGridCtrl.fsGridConfig.filter && fsGridCtrl.fsGridConfig.filter == 'zeroEmpty') {
                        return;
                    }
                }
                pattern = getPattern(formatType);
                value = formatService.formatPercentage(value, pattern);
                if (!isEmpty(value)) {
                    setter(fsGridCtrl.ngModel, formatService.parsePercentage(value));
                    $element.val(value);
                } else {
                    setter(fsGridCtrl.ngModel, '');
                    $element.val('');
                }
            }
        });
        /*if (formatType != 'date' && formatType != 'sensitive' && formatType != 'number') {
                $element.bind('keydown', function(e) {
                    function stopEvent(e) {
                        if(e.preventDefault){
                            e.preventDefault();
                        }else{
                            e.returnValue = false;
                        }
                    }
                    var theEvent = window.event || e;
                    var code = theEvent.keyCode || theEvent.which;
                    if (getter(fsGridCtrl) && ('' + getter(fsGridCtrl)).indexOf('.') >= 0 && (code == 110 || code == 190)) {
                        stopEvent(theEvent);
                    }
                    if (((code != 8 && code != 37 && code != 39 && code < 48) || (code > 57 && code < 96) || code > 105) && code != 110 && (code != 190 || theEvent.shiftKey == true) && !(code == 86 && theEvent.ctrlKey == true)) {
                        stopEvent(theEvent);
                    }
                });
            }*/
        if (formatType == 'number' || formatType == 'money' || formatType == 'percentage') {
            $element.bind('keydown', function (e) {
                function stopEvent(e) {
                    if (e.preventDefault) {
                        e.preventDefault();
                    } else {
                        e.returnValue = false;
                    }
                }
                var theEvent = window.event || e;
                var position = getCursortPosition($element[0]);
                var value = $element.val(),
                    isValidKey = true;
                var formatInfo = storageService.getFormatInfo() || {};
                if (fsGridCtrl.fsGridConfig && fsGridCtrl.fsGridConfig.options && fsGridCtrl.fsGridConfig.options.allowNegative) {
                    if (keydownService.minusKeyDown(e) && (isEmpty(value) || value.indexOf('-') == -1)) return;
                    if (position == 0 && value.indexOf('-') != -1) {
                        stopEvent(theEvent);
                        return;
                    }
                    if (keydownService.minusKeyDown(e) && position > 0) {
                        stopEvent(theEvent);
                        return;
                    }
                }

                pattern = getPattern(formatType);
                if (formatType == 'money') {
                    var ccyConfig = FormatterConstants.CCY_CONFIG,
                        zeros = '.000000000000000000000000000000000000000000000000',
                        fractionDigits;
                    formatInfo = storageService.getFormatInfo() || {};
                    if (ccyConfig[angular.uppercase(pattern)]) {
                        fractionDigits = ccyConfig[angular.uppercase(pattern)];
                        pattern = (formatInfo.money0 || '#,##0') + zeros.substr(0, fractionDigits + 1);
                    } else {
                        pattern = formatInfo[pattern];
                        pattern = pattern || formatInfo['defaultMoney'];
                    }
                } else if (formatType == 'percentage') {
                    pattern = formatInfo[pattern];
                    pattern = pattern || defaultFormat.percentage;
                } else if (formatType == 'number') {
                    pattern = formatInfo[pattern];
                    pattern = pattern || formatInfo.defaultFloat;
                }
                if (pattern.indexOf('.') >= 0) {
                    isValidKey = keydownService.floatKeyDown(e, value);
                } else {
                    isValidKey = keydownService.numKeyDown(e, value);
                }
                if (!isValidKey) {
                    stopEvent(theEvent);
                }
            });
        }
    }

    function getCursortPosition(textDom) {
        var cursorPos = 0;
        if (document.selection) {
            // IE Support
            textDom.focus();
            var selectRange = document.selection.createRange();
            selectRange.moveStart('character', -textDom.value.length);
            cursorPos = selectRange.text.length;
        } else if (textDom.selectionStart || textDom.selectionStart == '0') {
            // Firefox support
            cursorPos = textDom.selectionStart;
        }
        return cursorPos;
    }
})(angular);